package top.lingkang.hibernate5.dao;

import org.noear.solon.core.BeanBuilder;
import org.noear.solon.core.BeanWrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.Entity;
import javax.persistence.Table;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

/**
 * Dao接口的代理实现
 *
 * @author lingkang
 * created by 2023/9/28
 * @since 1.0.0
 */
public class DaoBeanBuilder implements BeanBuilder<Dao> {
    private static final Logger log = LoggerFactory.getLogger(DaoBeanBuilder.class);

    @Override
    public void doBuild(Class<?> clz, BeanWrap bw, Dao anno) throws Throwable {
        if (!clz.isInterface())
            throw new IllegalArgumentException("@Dao 只能作用在接口上，当前：" + clz.getName());

        Type[] interfaces = clz.getGenericInterfaces();
        Type type = interfaces[0];
        String className = getEntityClassName(type.getTypeName(), clz.getName());
        Class<?> entityClass = Class.forName(className);
        if (!entityClass.isAnnotationPresent(Table.class) && !entityClass.isAnnotationPresent(Entity.class)) {
            throw new IllegalStateException("缺少@Table/@Entity注解，HibernateDao设置的泛型类必须是映射实体类，当前类：" + entityClass.getName()
                    + " 所在的接口：" + clz.getName());
        }
        log.debug("dao interface: {}", className);

        Object proxyInstance = Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                new Class[]{clz}, new DaoInvocationHandler(clz, entityClass)
        );
        bw.rawSet(proxyInstance);
    }

    /**
     * top.lingkang.hibernate5.dao.HibernateDao<top.lingkang.h5.test.entity.UserEntity>
     * 将返回实体类：top.lingkang.h5.test.entity.UserEntity
     */
    private String getEntityClassName(String name, String interfaceName) {
        if (!name.endsWith(">")) {
            throw new IllegalStateException("HibernateDao必须设置好映射泛型实体类，例如：UserDao extends HibernateDao<UserEntity>，当前接口：" + interfaceName);
        }
        String substring = name.substring(41);
        return substring.substring(0, substring.length() - 1);
    }
}
